package com.day.newcorp;
import java.io.IOException;
import java.util.Dictionary;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.commons.auth.spi.AuthenticationHandler;
import org.apache.sling.commons.auth.spi.AuthenticationInfo;
import org.apache.sling.commons.osgi.OsgiUtil;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.newcorp.ccp.domain.Portal;
import com.newcorp.ccp.resolver.PortalUtil;


/**
 * @scr.component immediate="true" label="CCPortal 2.0 | CCP Authentication Handler configuration"
 *                description="CCPortal 2.0 | CCP Authentication Handler configuration"
 *                name="com.day.newcorp.CcpAuthenticationHandler"
 * @scr.service
 * 
 */
public class CcpAuthenticationHandler implements AuthenticationHandler {
	private final Logger log = LoggerFactory.getLogger(this.getClass().getName());
	/** @scr.property name="service.description" */
	private static final String DESCRIPTION = "CCP Authentication Handler";
	/** @scr.reference */
	private AuthUtil authUtil;

	/** @scr.property name="path" description="Default User Name" cardinality=200*/
	public static String path;

	/** @scr.property name="authenticatedPaths" description="All secured content" cardinality=500 */
	public static String[] ccpAuthenticatedPaths;

	/** @scr.reference */
	private ResourceResolverFactory resolverFactory;


	@SuppressWarnings("unchecked")
	protected void activate(ComponentContext componentContext) {		
		Dictionary<String, ?> props = componentContext.getProperties();		
		String[] paths = OsgiUtil.toStringArray(props.get(PATH_PROPERTY));		
		ccpAuthenticatedPaths = OsgiUtil.toStringArray(props.get("authenticatedPaths"));
	}

	/**
	 * redirects to the login page if UNAUTH user tries to access AUTH page or invalid cookie
	 * @param request HttpServletRequest request map
	 * @param response  HttpServletResponse
	 * @return true
	 * @throws IOException the exception
	 */
	public boolean requestCredentials(HttpServletRequest request, HttpServletResponse response) throws IOException {
		log.debug("CCP requestCredentials :" + request.getPathInfo());
		String failReason = (String)request.getAttribute("_FAIL");
		if (failReason == null){
			failReason = "";
		} else {
			if (failReason.equalsIgnoreCase("timeout")){
				failReason = "&errorCode=timeout";
			}
		}
	
		response.sendRedirect(getLoginPage(request)+ "?errorCode=session&" +failReason );
		
		return true;
	}

	/**
	 * Extract the credentials contained inside the request, parameter or cookie
	 * get called for every request as defined in PATH attribute
	 * MUST not return null. send back a false login
	 * @see com.day.cq.auth.impl.AbstractHTTPAuthHandler#authenticate(javax.servlet.http.HttpServletRequest,
	 *      javax.servlet.http.HttpServletResponse)
	 */
	public AuthenticationInfo extractCredentials( HttpServletRequest request, HttpServletResponse response) {
		try {
			log.info(String.format("extractCredentials Autheticating request Path: %s" , request.getPathInfo()));	
			String fileNameExtension = getFileNameExtension(request.getPathInfo());
			if(ArrayUtils.contains(CCPAuthConfig.ccpAuthExtensions, fileNameExtension)){
				//if(isEligibleResourceForAuth(request)){
								
				AuthenticationStatus authenticationStatus = authUtil.authenticateRequest(request, response);
				String status = authenticationStatus.getValidationStatus();
				authUtil.createOrUpdateCCPCookie(request,response,status);
				boolean isAuthURL =  isAuthorizedURL(request);				
				if(isAuthURL && StringUtils.equalsIgnoreCase(status, AuthenticationUtil.UNAUTH_USER)) {
					//unauth user can not access authenticated page
					log.info("extractCredentials UNAUTH_USER AUTH URL");
					request.setAttribute("isUserAuthenticated", "N");
					request.setAttribute("_FAIL", "NOAUTH");
					return new AuthenticationInfo("FAIL","FAIL");					
				} else if(StringUtils.equalsIgnoreCase(status, AuthenticationUtil.VALIDATION_SUCCESS)) {
					log.info("extractCredentials AUTH_USER");
					request.setAttribute("isUserAuthenticated", "Y");
					return new AuthenticationInfo(HttpServletRequest.FORM_AUTH,	CCPAuthConfig.defaultUserName,CCPAuthConfig.defaultUserPassword.toCharArray());				
				} else if(StringUtils.equalsIgnoreCase(status, AuthenticationUtil.INVALID_LOGIN_TOKEN)) {
					log.info("extractCredentials AUTH_USER INVALID_LOGIN_TOKEN");
					request.setAttribute("isUserAuthenticated", "N");
					return new AuthenticationInfo(HttpServletRequest.FORM_AUTH,	CCPAuthConfig.defaultUserName,CCPAuthConfig.defaultUserPassword.toCharArray());				
				}

			}else {
				
				//log.info("extractCredentials doing nothing for URL =" + request.getPathInfo());
			}

		} catch (Exception exp) {
			log.error(String.format("extractCredentials Error in Autheticating request Path: %s" , request.getPathInfo()), exp);

		}
		
		return new AuthenticationInfo(HttpServletRequest.FORM_AUTH,	CCPAuthConfig.defaultUserName,CCPAuthConfig.defaultUserPassword.toCharArray());
		
	}



	/**
	 * returns the login page en.html of requested portal.	 * 
	 * @param request HttpServletRequest	 
	 * @return login page name as configured in portal resolver  
	 */	
	protected String getLoginPage(HttpServletRequest request) {
		Portal portal = PortalUtil.getPortal(request);
		if(portal != null) {
			return portal.getPortalLoginPage();
		}

		return null;
	}

	public void dropCredentials(HttpServletRequest arg0, HttpServletResponse arg1) throws IOException {
		// TODO Auto-generated method stub
	}

	/**
	 * Deactivate this component.
	 * @param componentContext
	 */
	protected void deactivate(ComponentContext componentContext) {
		//TODO: need to identify cleanup task for this authentication handler
	}

	/**
	 * checks whether request URL.pathinfo is in authenticated path list(ccpAuthenticatedPaths) 
	 * @param request HttpServletRequest	 
	 * @return true if auth url else false  
	 */	
	private boolean isAuthorizedURL(HttpServletRequest request){
		String requestedPath = request.getPathInfo();
		boolean isAuthURL = false;
		//String requestedPath ="/walmart/en/claims/products.html";
		//String requestedPath ="/walmart/en/faq.html";

		//remove .html
		for(String ext : CCPAuthConfig.ccpAuthExtensions) {
			requestedPath = requestedPath.replace("." +ext , "");
		}				
		String[] urlpathParts = requestedPath.split("/");
		//do not consider nothing/walmart/ 
		if(urlpathParts.length > 3) {
			String pathTovalidate="/";
			for(int i=2;i<urlpathParts.length;i++) {
				pathTovalidate += urlpathParts[i];				
				if(ArrayUtils.contains(ccpAuthenticatedPaths, pathTovalidate)){
					isAuthURL= true;
					break;
				}
				pathTovalidate += "/";
			}
		}

		log.info(String.format("isAuthorizedURL Path: %s and isAuthorized: %s" , request.getPathInfo(),isAuthURL));
		return isAuthURL;
	}

	/**
	 * returns the extension of requested url
	 * example: path /walmart/en.html will return html
	 * @param pathInfo from the request	 
	 * @return the last part of request URL pathInfo
	 * 
	 */	
	private String getFileNameExtension(String pathInfo) {		
		int slashIndex = pathInfo.lastIndexOf('/');
		int dotIndex = pathInfo.lastIndexOf('.');
		String filenameExtension;
		if (dotIndex == -1)	{
			filenameExtension = pathInfo.substring(slashIndex + 1);
		} else{
			filenameExtension = pathInfo.substring(dotIndex+ 1);
		}
		return filenameExtension;
	}

	private boolean isEligibleResourceForAuth(HttpServletRequest request) {
		String requestUrl = request.getPathInfo();
		ResourceResolver resourceResolver = null;

		try {
			resourceResolver = resolverFactory.getAdministrativeResourceResolver(null);
			if(resourceResolver != null) {
				Resource res = resourceResolver.resolve(requestUrl);
				if(res.getResourceType().equalsIgnoreCase("cq:Page")){				
					return true;
				}
			}
		} catch (LoginException e) {
			log.error("CcpAuthenticationHandler isEligibleResource LoginException " ,e );
		} finally {
			if(resourceResolver != null) {
				resourceResolver.close();
			}
		}

		return false;

	}


	@Override
	public String toString() {
		return DESCRIPTION;
	}
}
